package com.df.my_net_game.game_rpc;

import com.df.my_net_game.game_rpc.client.lb.LBStrategy;
import com.df.my_net_game.game_rpc.client.lb.impl.LastFirstLBStrategy;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;

/**
 * RPC服务抽象
 * 1. 管理服务节点集合
 * 2. 实现负载均衡策略
 * 3. 节点健康状态管理
 */
public class RpcService {
    private final String serviceName;
    private final List<ServerNode> serverNodes = new CopyOnWriteArrayList<>();
    private final Map<String, ServerNode> nodeMap = new ConcurrentHashMap<>();
    private LBStrategy lbStrategy = new LastFirstLBStrategy();
    private Timer healthTimer;

    public RpcService(String serviceName) {
        this.serviceName = serviceName;
        initHealthCheck();
    }

    public synchronized void addNode(ServerNode node) {
        node.setServiceName(serviceName);
        if (!nodeMap.containsKey(node.getAddress())) {
            serverNodes.add(node);
            nodeMap.put(node.getAddress(), node);
        }
    }

    public void removeNode(String address) {
        ServerNode node = nodeMap.remove(address);
        if (node != null) {
            serverNodes.remove(node);
            node.close();
        }
    }

    public ServerNode selectNode() {
        List<ServerNode> healthyNodes = new ArrayList<>(serverNodes.size());
        for (ServerNode node : serverNodes) {
            if (node.isHealthy()) {
                healthyNodes.add(node);
            }
        }
        if (healthyNodes.isEmpty()) {
            throw new RuntimeException("No available nodes for service: " + serviceName);
        }
        return lbStrategy.select(healthyNodes);
    }

    private void initHealthCheck() {
        healthTimer = new Timer("HealthCheck-" + serviceName, true);
        healthTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                checkNodesHealth();
            }
        }, 0, TimeUnit.SECONDS.toMillis(30));
    }

    private void checkNodesHealth() {
        for (ServerNode node : serverNodes) {
            try {
                boolean alive = node.checkAlive(5000);
                if (!alive) {
                    node.markUnhealthy();
                }
            } catch (Exception e) {
                removeNode(node.getAddress());
            }
        }
    }

    public void shutdown() {
        if (healthTimer != null) {
            healthTimer.cancel();
        }
        serverNodes.forEach(ServerNode::close);
        serverNodes.clear();
        nodeMap.clear();
    }

    // Getters and Setters
    public LBStrategy getLbStrategy() { return lbStrategy; }
    public void setLbStrategy(LBStrategy lbStrategy) { this.lbStrategy = lbStrategy; }
    public List<ServerNode> getServerNodes() { return Collections.unmodifiableList(serverNodes); }
}
